package com.unicom.office.parser;

import com.unicom.office.config.*;
import com.unicom.office.dataitem.DataItem;
import com.unicom.office.dataitem.NumberDataItem;
import com.unicom.office.dataitem.StringDataItem;
import com.unicom.office.func.*;
import com.unicom.office.node.*;
import com.unicom.office.templatefunc.TemplateBase;
import com.unicom.office.templatefunc.TemplateFunc;
import com.unicom.office.templateproc.TemplateProc;
import com.unicom.office.type.*;
import com.unicom.office.var.NumberVarItem;
import com.unicom.office.var.StringVarItem;
import com.unicom.office.var.VarItem;

import java.util.ArrayList;
import java.util.List;

public class OfficeSyntaxParser {
    private String beginMarker = "$";
    private String endMarker = "";
    private ParagraphNode paragraphNode;
    private OfficeConfig config;
    private int currentItemIndex = 0;
    private int currentTextIndex = -1;
    private List<FuncBase> tempChildren;

    public OfficeSyntaxParser(ParagraphNode paragraphNode, OfficeConfig config){
        this.paragraphNode = paragraphNode;
        this.config = config;
        this.tempChildren = new ArrayList<FuncBase>();
    }

    public void parse() {
        while(hasNextToken()){
            if(judgeBeginMarker()){
                // 如果匹配到开始标记，开始匹配字符
                marker();
                endMarker();
            }else{
                // 没有匹配到开始标记，则按照普通文本处理
                text();
            }
        }

        paragraphNode.getChildren().clear();
        for (FuncBase tempChild : tempChildren) {
            paragraphNode.addChild(tempChild);
        }
    }

    private boolean judgeBeginMarker() {
        int i = 0;
        while(i < beginMarker.length()){
            Token token = nextToken();
            if(beginMarker.charAt(i) != token.getChar()){
                prevToken(i + 1);
                return false;
            }
            i++;
        }

        return true;
    }

    private void marker() {
        TypeItem typeItem = null;
        Object formula = formula(typeItem);
        // 用来检测是不是有类型标记
        if(formula instanceof TypeItem) {
            typeItem = (TypeItem)formula;
            formula = formula(typeItem);
        }

        if(!(formula instanceof FuncCommon)) {
            throw new RuntimeException("无效的标记");
        }

        FuncCommon funcNode = (FuncCommon) formula;

        //
        if(funcNode.getType() instanceof DataTypeItem){
            funcNode = new TextRangeFunc(currentToken().getValue().getSource(), (FuncBase) funcNode);
            tempChildren.add((FuncBase)funcNode);
        }else if(funcNode.getType() == TextRangeTypeItem.INSTANCE){
            tempChildren.add((FuncBase)funcNode);
        }else if(funcNode.getType() instanceof OfficeTypeItem){
            preHandle.handle(paragraphNode, funcNode);
            tempChildren.add(BlankTextRangeFunc.INSTANCE);
        }
    }

    private FuncCommon func(TypeItem typeItem){
        FuncCommon funcNode = null;
        String funcName = name();

        // 先判断是不是关键字
        if("if".equals(funcName)){
            return condition(typeItem);
        }

        // 判断是不是函数
        if(hasNextToken()) {
            Token token = nextToken();
            if(token.getChar() == '('){
                TemplateBase templateFunc = config.getTemplateFunc(funcName);
                if(templateFunc instanceof TemplateFunc) {
                    FuncNode temp = new FuncNode((TemplateFunc) templateFunc);
                    temp.addParams(params(typeItem));
                    funcNode = temp;
                }else {
                    ProcNode temp = new ProcNode((TemplateProc)templateFunc);
                    temp.addParams(params(typeItem));
                    funcNode = temp;
                }
            }else{
                prevToken();
            }
        }

        // 到这里可以判断是变量了
        if(funcNode == null){
            VarItem varItem = config.getVarItem(funcName);
            funcNode = new VarFunc(varItem);
        }

        if(funcNode instanceof ProcNode){
            return funcNode;
        }

        // 进一步判断是不是要访问子属性
        if(hasNextToken()){
            Token token = nextToken();
            if(token.getChar() == '.'){
                String propName = name();
                FuncBase temp = (FuncBase) funcNode;
                ObjectPropFunc objectPropFunc = new ObjectPropFunc();
                objectPropFunc.setObjectFunc(temp);
                ObjectTypeItem type = (ObjectTypeItem) temp.getType();
                objectPropFunc.setKey(type.getProp(propName));
                funcNode = objectPropFunc;
            }else if(token.getChar() == '['){
                Object propObj = formula(typeItem);
                FuncBase temp = (FuncBase) funcNode;
                ListItemFunc listItemFunc = new ListItemFunc();
                listItemFunc.setListFunc(temp);
                listItemFunc.setIndexObj(propObj);
                funcNode = listItemFunc;
                judgeToken(']');
            }else{
                prevToken();
            }
        }

        return funcNode;
    }

    private List<Object> params(TypeItem typeItem){
        List<Object> list = new ArrayList<Object>();

        Token token = nextToken();

        if(token.getChar() == ')'){
            return list;
        }else {
            prevToken();
        }

        Object param = formula(typeItem);
        list.add(param);

        while(hasNextToken()){
            Token token1 = nextToken();
            if(token1.getChar() == ','){
                Object param1 = formula(typeItem);
                list.add(param1);
            }else if(token1.getChar() == ')'){
                return list;
            }else{
                throw new RuntimeException("错误的参数标记");
            }
        }

        throw new RuntimeException("未发现函数结束标记");
    }

    private String name(){
        String value = "";
        while (hasNextToken())
        {
            Token token = nextToken();
            if (!token.isPicture() && "_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".indexOf(token.getChar()) != -1)
            {
                value += token.getChar();
            }
            else
            {
                prevToken();
                break;
            }
        }
        if(value.length() == 0){
            throw new RuntimeException("无效的函数或变量名称");
        }
        return value;
    }

    private Object formula(TypeItem typeItem){
        Token token = nextToken();

        if(token.isPicture()) {
            return token.getValue();
        }

        char tokenChar = token.getChar();

        if(tokenChar == '('){
            return codeBlock(typeItem);
        }else if(tokenChar == '<'){
            return itemType();
        } else if(tokenChar == '`'){
            return str();
        }else if("0123456789".indexOf(tokenChar) != -1){
            prevToken();
            return num();
        }else if("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(tokenChar) != -1){
            prevToken();
            return func(typeItem);
        }else if(tokenChar == '@'){
            return iterator(typeItem);
        }else{
            throw new RuntimeException("错误的表达式标记");
        }
    }

    private TypeItem itemType(){
        String typeName = name();
        judgeToken('>');
        if("cell".equals(typeName)){
            return CellTypeItem.INSTANCE;
        }else if("row".equals(typeName)){
            return RowTypeItem.INSTANCE;
        }else if("paragraph".equals(typeName)){
            return ParagraphTypeItem.INSTANCE;
        }else if("worksheet".equals(typeName)){
            return WorksheetTypeItem.INSTANCE;
        }else if("workbook".equals(typeName)){
            return WorkbookTypeItem.INSTANCE;
        } else {
            throw new RuntimeException("不支持的类型");
        }
    }

    private void endMarker() {
        for (int i = 0; i < endMarker.length(); i++) {
            Token token = nextToken();
            if(endMarker.charAt(i) != token.getChar()) {
                throw new RuntimeException("未发现有效结束标记");
            }
        }
    }

    private void text() {
        appender(tempChildren, nextToken());
    }

    private DataItem num(){
        String valueStr = "";
        boolean hasPoint = false;
        while (hasNextToken())
        {
            Token token = nextToken();
            if (!token.isPicture() && ".0123456789".indexOf(token.getChar()) != -1)
            {
                if(token.getChar() == '.'){
                    if(hasPoint) {
                        throw new RuntimeException("多次出现小数点");
                    }else{
                        hasPoint = true;
                    }
                }
                valueStr += token.getChar();
            }else {
                prevToken();
                break;
            }
        }
        return NumberDataItem.create(valueStr);
    }

    private Object codeBlock(TypeItem typeItem){
        Object value = formula(typeItem);
        judgeToken(')');
        return value;
    }

    private DataItem str(){
        List<OfficeNode> value = new ArrayList<OfficeNode>();
        while (hasNextToken())
        {
            Token token = nextToken();

            if (token.getChar() == '`')
            {
                break;
            }
            else
            {
                appender(value, token);
            }
        }

        RichString richString = new RichString();
        for (OfficeNode item : value) {
            richString.addChild(item);
        }
        return StringDataItem.create(richString);
    }

    private IfFunc condition(TypeItem typeItem){
        judgeToken('(');
        IfFunc ifFunc = new IfFunc(typeItem);
        ifFunc.setExpression((FuncBase) formula(typeItem));
        judgeToken(')');
        return ifFunc;
    }

    private ForFunc iterator(TypeItem typeItem){
        // 如何让函数提升到它所应用的元素上去呢？如果是函数，则往上找元素，然后将元素放进该函数中。指定元素类型$r:@list@item@index
        FuncBase listObj = (FuncBase)formula(typeItem);
        judgeToken('@');
        String itemName = name();
        String indexName = "";
        if(hasNextToken()){
            Token token = nextToken();
            if(token.getChar() == '@'){
                indexName = name();
            }else{
                prevToken();
            }
        }
        ForFunc forFunc = new ForFunc(typeItem);
        forFunc.setList(listObj);
        ListTypeItem listType = (ListTypeItem)listObj.getType();
        VarItem item = listType.getItemType().createVarItem(itemName);
        config.register(item);
        forFunc.setItemName(item);
        if(!"".equals(indexName)) {
            VarItem index = NumberTypeItem.INSTANCE.createVarItem(indexName);
            config.register(index);
            forFunc.setIndexName(index);
        }
        return forFunc;
    }

    private void appender(List list, Token token) {
        if (!token.isPicture() && list.size() > 0 && list.get(list.size() - 1) instanceof TextRangeNode) {
            TextRangeNode last = (TextRangeNode) list.get(list.size() - 1);
            if (last.getProcs().size() == 0 && last.getSource() == token.getValue().getSource())
            {
                last.appendChar(token.getChar());
                return;
            }
        }

        list.add(token.getValue());
    }

    private boolean hasNextToken(){
        if(paragraphNode.getChildren().size() == 0) {
            return false;
        }else if(currentItemIndex < paragraphNode.getChildren().size() - 1) {
            return true;
        }

        FuncBase currentNode = paragraphNode.getChildren().get(currentItemIndex);

        if(currentNode instanceof PictureNode){
            return false;
        }else{
            TextRangeNode textRangeNode = (TextRangeNode)currentNode;
            if(currentTextIndex < textRangeNode.getText().length() - 1) {
                return true;
            }else{
                return false;
            }
        }
    }

    private Token nextToken(){

        FuncBase currentNode = paragraphNode.getChildren().get(currentItemIndex);

        // 将指针调整到下一个token
        if(currentNode instanceof PictureNode) {
            currentItemIndex++;
            currentTextIndex = 0;
        }else if(currentNode instanceof TextRangeNode){
            TextRangeNode textRangeNode = (TextRangeNode)currentNode;
            currentTextIndex++;
            if(textRangeNode.getText().length() == currentTextIndex) {
                currentItemIndex++;
                currentTextIndex = 0;
            }
        }

        if(paragraphNode.getChildren().size() == currentItemIndex) {
            throw new RuntimeException("没有下一个token了");
        }

        return currentToken();
    }

    private void prevToken(){
        if(currentItemIndex == 0) {
            currentTextIndex--;
        }else {
            if(currentTextIndex == 0) {
                currentItemIndex--;
            }

            FuncBase currentNode = paragraphNode.getChildren().get(currentItemIndex);

            if(currentNode instanceof TextRangeNode){
                TextRangeNode textRangeNode = (TextRangeNode)currentNode;
                if(currentTextIndex == 0) {
                    currentTextIndex = textRangeNode.getText().length();
                }
                currentTextIndex--;
            }
        }
    }

    private void prevToken(int count) {
        for (int i = 0; i < count; i++) {
            prevToken();
        }
    }

    private void judgeToken(char c){
        Token token = nextToken();
        if(c != token.getChar()) {
            throw new RuntimeException("不符合预期");
        }
    }

    private Token currentToken(){
        FuncBase currentNode = paragraphNode.getChildren().get(currentItemIndex);
        if(currentNode instanceof PictureNode){
            return new Token((OfficeNode) currentNode);
        }else{
            TextRangeNode textRangeNode = (TextRangeNode)currentNode;
            TextRangeNode newTextRangeNode = (TextRangeNode) textRangeNode.clone();
            newTextRangeNode.setText(textRangeNode.getText().charAt(currentTextIndex), false);
            return new Token(newTextRangeNode);
        }
    }
}
